#include "./dcpu.h"
#include "./hw_interface.h"
#include <time.h>
#define LREG(x) lua_register(lua, #x, x)

hw_device *HW[MAX_HW];
unsigned short HWC; //Device count
lua_State *lua_hw;
extern unsigned short REG[8];
extern unsigned short RAM[RAM_SIZE];
extern unsigned short pc;
extern unsigned short sp;
extern unsigned short ia;
extern bool QUEUEING;
extern IQN *IQL;
extern IQN *IQH;
extern unsigned short IQC;

void sendInterrupt(hw_device *target){
	//Activate the lua action
	if(!target) return;
	target->init = 1;
	//call the interrupt function
	lua_getglobal(target->lua, "interrupt");
	lua_pcall(target->lua, 0, 0, 0);
}

void sendUpdate(hw_device *target){
	if(!target) return;
	if(!target->init) return;
	lua_getglobal(target->lua, "update");
	lua_pcall(target->lua, 0, 0, 0);
}

void openLibHW(lua_State *lua){
	LREG(setA);
	LREG(setB);
	LREG(setC);
	LREG(setX);
	LREG(setY);
	LREG(setZ);
	LREG(setI);
	LREG(setJ);
	LREG(setRAM);
	LREG(readA);
	LREG(readB);
	LREG(readC);
	LREG(readX);
	LREG(readY);
	LREG(readZ);
	LREG(readI);
	LREG(readJ);
	LREG(readRAM);
	LREG(push);
	LREG(pop);
	LREG(peek);
	//LREG(pick);
	LREG(setSP);
	LREG(setPC);
	LREG(loadDevice);
	LREG(issueHWI);
	LREG(ptime);
};

int setA(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[0] = n;
	return -1;
}
int setB(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[1] = n;
	return -1;
}
int setC(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[2] = n;
	return -1;
}
int setX(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[3] = n;
	return -1;
}
int setY(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[4] = n;
	return -1;
}
int setZ(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[5] = n;
	return -1;
}
int setI(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[6] = n;
	return -1;
}
int setJ(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	REG[7] = n;
	return -1;
}
int setRAM(lua_State *lua){
	unsigned int addr = 0;
	unsigned short n = 0;
	if(lua_isnumber(lua, 1) && lua_isnumber(lua, 2)){
		addr = lua_tonumber(lua, 1);
		n = lua_tonumber(lua, 2);
	}	else return 0;
	lua_pop(lua, 2);
	if(addr < RAM_SIZE)
		RAM[addr] = n;
	return -2;
}

int readA(lua_State *lua){
	lua_pushnumber(lua, REG[0]);
	return 1;
}
int readB(lua_State *lua){
	lua_pushnumber(lua, REG[1]);
	return 1;
}
int readC(lua_State *lua){
	lua_pushnumber(lua, REG[2]);
	return 1;
}
int readX(lua_State *lua){
	lua_pushnumber(lua, REG[3]);
	return 1;
}
int readY(lua_State *lua){
	lua_pushnumber(lua, REG[4]);
	return 1;
}
int readZ(lua_State *lua){
	lua_pushnumber(lua, REG[5]);
	return 1;
}
int readI(lua_State *lua){
	lua_pushnumber(lua, REG[6]);
	return 1;
}
int readJ(lua_State *lua){
	lua_pushnumber(lua, REG[7]);
	return 1;
}
int readRAM(lua_State *lua){
	unsigned short addr = 0;
	if(lua_isnumber(lua, 1))
		addr = lua_tonumber(lua, 1);
	else return 0;
	lua_pushnumber(lua, RAM[addr]);
	return 1;
}


int push(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	_push(&n);
	return -1;
}
int pop(lua_State *lua){
	unsigned short n = 0;
	_pop(&n);
	lua_pushnumber(lua, n);
	return 1;
}
int peek(lua_State *lua){
	unsigned short n = 0;
	_peek(&n);
	lua_pushnumber(lua, n);
	return 1;
}
int setSP(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	sp = n;
	return -1;
}
int setPC(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	pc = n;
	return -1;
}
int loadDevice(lua_State *lua){
	//Populate a device structure
	//FILE, MAN, ID, VER
	if(!lua_isstring(lua, 1) || !lua_isnumber(lua, 2) || !lua_isnumber(lua, 3) || !lua_isnumber(lua, 4)) return 0;
	HW[HWC] = new hw_device;
	size_t size;
	const char *str;
	str = lua_tolstring(lua, 1, &size); 
	HW[HWC]->def_file = new char[size];
	strcpy(HW[HWC]->def_file, str);
	HW[HWC]->man = lua_tonumber(lua, 2);
	HW[HWC]->id = lua_tonumber(lua, 3);
	HW[HWC]->ver = lua_tonumber(lua, 4);
	HW[HWC]->init = 0;
	HW[HWC]->lua = luaL_newstate();
	luaL_openlibs(HW[HWC]->lua);
	openLibHW(HW[HWC]->lua);
	luaL_dofile(HW[HWC]->lua, HW[HWC]->def_file);

	++HWC;
	return 0;
}
int issueHWI(lua_State *lua){
	unsigned short n = 0;
	if(lua_isnumber(lua, 1))
		n = lua_tonumber(lua, 1);
	else return 0;
	lua_pop(lua, 1);
	//n is our message
	//if ia == 0, ignore the HWI
	if(ia != 0){
		if(QUEUEING){
				IQN *nt = new IQN;
				nt->msg = n;
				nt->next = NULL;
				IQL->next = nt;
				IQL = nt;
				++IQC;
				if(IQC == INTERRUPT_SIZE){
					std::cout << "Interrupt Queue Exceeded, DCPU-16 has caught fire" << std::endl;
					exit(0);
				}
			}else{
				QUEUEING = true; //This prevents Interrupt chaining
				_push(&pc);
				_push(&REG[0]);
				pc = ia;
				REG[0] = n;
			}
	}
	return -1;
}

int ptime(lua_State *lua)
{
	lua_pushnumber(lua, clock() / (CLOCKS_PER_SEC / 1000));
	return 1;
}